#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <time.h>

#include <debug.h>
#include <os_api.h>

#define TASK_STACK_SIZE (20 * 1024)
static char _task_stack[TASK_STACK_SIZE] __align(4);

static void app_test_ctype()
{
    int test[] = {0, 0x7, ' ', '#', '0', '<', 'A', '-', 'a', 0x7f};
    int ret;
    size_t n;

    LOG_PRINTF("%s: +", __func__);

    for (n = 0; n < (sizeof(test) / sizeof(test[0])); n++)
    {
        ret = isalnum(test[n]);
        LOG_PRINTF("%s: isalnum (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isalpha(test[n]);
        LOG_PRINTF("%s: isalpha (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = iscntrl(test[n]);
        LOG_PRINTF("%s: iscntrl (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isdigit(test[n]);
        LOG_PRINTF("%s: isdigit (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isgraph(test[n]);
        LOG_PRINTF("%s: isgraph (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = islower(test[n]);
        LOG_PRINTF("%s: islower (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isprint(test[n]);
        LOG_PRINTF("%s: isprint (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = ispunct(test[n]);
        LOG_PRINTF("%s: ispunct (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isspace(test[n]);
        LOG_PRINTF("%s: isspace (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isupper(test[n]);
        LOG_PRINTF("%s: isupper (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = isxdigit(test[n]);
        LOG_PRINTF("%s: isxdigit (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
    }

    for (n = 0; n < (sizeof(test) / sizeof(test[0])); n++)
    {
        ret = tolower(test[n]);
        LOG_PRINTF("%s: tolower (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
        ret = toupper(test[n]);
        LOG_PRINTF("%s: toupper (%c) 0x%x return 0x%x", __func__, isprint(test[n]) ? test[n] : '?', test[n], ret);
    }

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_math()
{
    int p;
    double d, dp;

    LOG_PRINTF("%s: +", __func__);

#define _test(a) \
    LOG_PRINTF("%s: %s return %f", __func__, #a, (a));

    _test(atan(0.5));
    _test(cos(3.14));
    _test(sin(3.14 / 4));
    _test(tan(3.14 / 4));
    _test(tanh(2.2));

    d = frexp(2.2, &p);
    LOG_PRINTF("%s: frexp(2.2) return %f %d", __func__, d, p);
    d = modf(2.2, &dp);
    LOG_PRINTF("%s: frexp(2.2) return %f %f", __func__, d, dp);

    _test(ceil(2.2));
    _test(fabs(-2.2));
    _test(floor(2.2));

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_string()
{
    char a[512] = "abcdefgABCDEFG1234567890Abc-----------------------------------------------------=============================================", *p;

    LOG_PRINTF("%s: +", __func__);

    LOG_PRINTF("%s: string is %s, string addr %p", __func__, a, a);
    p = strchr(a, 'A');
    LOG_PRINTF("%s: strchr 'A' return %p", __func__, p);
    p = strchr(a, 'Q');
    LOG_PRINTF("%s: strchr 'Q' return %p", __func__, p);

    p = strrchr(a, 'A');
    LOG_PRINTF("%s: strrchr 'A' return %p", __func__, p);
    p = strrchr(a, 'Q');
    LOG_PRINTF("%s: strrchr 'Q' return %p", __func__, p);

    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    p = strdup(a);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    if (p)
        free(p);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_stdio()
{
    char buf[256], *p;
    double d = 26.2;
    int ret;

    LOG_PRINTF("%s: +", __func__);

    snprintf(buf, sizeof(buf), "abcdefg %d, %f", 20, d);
    LOG_PRINTF("%s: %s", __func__, buf);

    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    ret = asprintf(&p, "test is a test for asprintf %u %f", 15, 99.99999);
    if (ret < 0)
        p = NULL;
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    if (p)
        free(p);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_stdlib()
{
    char *p;

    LOG_PRINTF("%s: +", __func__);

    LOG_PRINTF("%s: atoi(\"20\") return %d", __func__, atoi("20"));
    LOG_PRINTF("%s: atoi(\"\") return %d", __func__, atoi(""));
    LOG_PRINTF("%s: atoi(\"abc\") return %d", __func__, atoi("abc"));
    LOG_PRINTF("%s: atoi(\"-56abc\") return %d", __func__, atoi("-56abc"));

    LOG_PRINTF("%s: strtol(\"132\", NULL, 10) return %ld", __func__, strtol("132", NULL, 10));
    LOG_PRINTF("%s: strtol(\"13269AB\", NULL, 10) return %ld", __func__, strtol("13269AB", NULL, 10));
    LOG_PRINTF("%s: strtol(\"\", NULL, 10) return %ld", __func__, strtol("", NULL, 10));
    LOG_PRINTF("%s: strtol(\"132\", NULL, 16) return %ld", __func__, strtol("132", NULL, 16));
    LOG_PRINTF("%s: strtol(\"13269AB\", NULL, 16) return %ld", __func__, strtol("13269AB", NULL, 16));

    LOG_PRINTF("%s: strtod(\"132.34513789789898989089080090\", NULL, 10) return %f", __func__, strtod("132.34513789789898989089080090", NULL));
    LOG_PRINTF("%s: strtod(\"13269AB\", NULL, 10) return %f", __func__, strtod("13269AB", NULL));

    srand(time(NULL));
    LOG_PRINTF("%s: rand %d %d %d %d", __func__, rand(), rand(), rand(), rand());

    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    p = malloc(1024);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    if (p)
        free(p);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    p = calloc(16, 1024);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    p = realloc(p, 32 * 1024);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    if (p)
        free(p);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_time()
{
    time_t t_cur;
    struct tm tm_cur;

    LOG_PRINTF("%s: +", __func__);

    time(&t_cur);
    LOG_PRINTF("%s: time %ld", __func__, t_cur);

    localtime_r(&t_cur, &tm_cur);
    LOG_PRINTF("%s: localtime %04d-%02d-%02d %02d:%02d:%02d yday %d wday %d", __func__, tm_cur.tm_year + 1900, tm_cur.tm_mon + 1, tm_cur.tm_mday,
               tm_cur.tm_hour, tm_cur.tm_min, tm_cur.tm_sec, tm_cur.tm_yday, tm_cur.tm_wday);

    gmtime_r(&t_cur, &tm_cur);
    LOG_PRINTF("%s: gmtime %04d-%02d-%02d %02d:%02d:%02d yday %d wday %d", __func__, tm_cur.tm_year + 1900, tm_cur.tm_mon + 1, tm_cur.tm_mday,
               tm_cur.tm_hour, tm_cur.tm_min, tm_cur.tm_sec, tm_cur.tm_yday, tm_cur.tm_wday);

    LOG_PRINTF("%s: -", __func__);
}

static void app_test_mem()
{
    typedef struct mem_list_s
    {
        struct mem_list_s *next;
    } mem_list_t;

    mem_list_t *head = NULL, *p;
    size_t sz = 0;

    LOG_PRINTF("%s: +", __func__);
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    while (sz < (2 * 1024 * 1024))
    {
        p = malloc(10 * 1024);
        if (!p)
            break;
        p->next = head;
        head = p;
        sz += 10 * 1024;
        // LOG_PRINTF("%s: alloced 10K %p", __func__, p);
    }

    LOG_PRINTF("%s: total alloced %u", __func__, sz);

    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());

    while (head)
    {
        p = head;
        head = head->next;
        free(p);
    }
    LOG_PRINTF("%s: system heap total: %u free: %u", __func__, OsaGetDefaultMemPoolTotalSize(), OsaGetDefaultMemPoolFreeSize());
    LOG_PRINTF("%s: -", __func__);
}

static void app_task(void *param)
{
    UINT32 test_step = 0;

    sleep(5); // 5 seconds

    while (1)
    {
        LOG_PRINTF("%s: test_step %d", __func__, test_step);
        if (test_step == 0)
        {
            app_test_ctype();
        }
        else if (test_step == 1)
        {
            app_test_math();
        }
        else if (test_step == 2)
        {
            app_test_string();
        }
        else if (test_step == 3)
        {
            app_test_stdio();
        }
        else if (test_step == 4)
        {
            app_test_stdlib();
        }
        else if (test_step == 5)
        {
            app_test_time();
        }
        else if (test_step == 6)
        {
            app_test_mem();
        }
        test_step++;

        sleep(5); // 5 seconds
    }
}

int main()
{
    int ret;
    OSTaskRef task_ref; // because following task will never exit, so we do not use task handle, so we don't care it's handle

    ret = OSATaskCreate(&task_ref, _task_stack, TASK_STACK_SIZE, OS_APP_PRIORITY, "app", app_task, NULL);
    ASSERT(ret == OS_SUCCESS);
}
